﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
using System.IO;
using System.Threading;
using System.Web.Script.Serialization;
using System.Diagnostics;

namespace Core
{
    public class Analyzer
    {
        public const string querryMask = "C# \"{2}\""; // mask for creating query

        public List<KeyValuePair<double, SearchResult>> RunAnalysis(string error, string selectedLine, string exceptionMessage, string exceptionSource) 
        {
            return RunAnalysis(string.Format(querryMask, exceptionSource, error, exceptionMessage), exceptionMessage);
        }

        public List<KeyValuePair<double, SearchResult>> RunAnalysis(string searchString, string message)
        {
            Search searcher = new Search();          

            //The results format is: 
            //0: Stack Overflow, 1: Experts Exchange, 2: CodeGuru, 3: Bytes, 4: DaniWeb           
            List<List<SearchResult>> results = searcher.PerformSearch(searchString, 10);
            List<KeyValuePair<double, SearchResult>> rankedResults = new List<KeyValuePair<double, SearchResult>>();

            //Now we have the results, fetch each page and run it through the rules
            IRule[] rules = RuleFinder.FindAllRules();
                        
            int totalCnt = 0;
            var threads = new List<Thread>();
            for (int i = 0; i < results.Count; i++)
            {
                totalCnt += results[i].Count;
                List<SearchResult> siteResults = results[i];
                Rules.SiteEnum siteNum = (Rules.SiteEnum)i + 1;
                var thr = new Thread(ApplyRules);
                threads.Add(thr);
                thr.Name = string.Format("Search_{0}_{1}", i, DateTime.Now.Ticks);
                thr.Start(new object[] { siteResults, siteNum, rankedResults, rules, message });
            }
            for (int i = 0; i < threads.Count; i++)
            {
                threads[i].Join();
            }
            rankedResults.Sort(DescSortByRankValue);
            
            return rankedResults;
        }

        /// <summary>
        /// Sort function for our list. (descending sorting)
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static int DescSortByRankValue(KeyValuePair<double, SearchResult> a, KeyValuePair<double, SearchResult> b) 
        {
            if (a.Key > b.Key) return -1;
            else return 1;
        }


        private void ApplyRules(object parameters)
        {
            var arr = parameters as object[];
            if (arr.Length != 5) throw new ArgumentException("Incorrect argument (wrong lenght of the array)");
            var siteResults = arr[0] as List<SearchResult>;
            var siteNum = (Rules.SiteEnum)arr[1];
            var rankedResults = arr[2] as List<KeyValuePair<double, SearchResult>>;
            var rules = arr[3] as IRule[];
            var message = arr[4] as string;

            foreach (SearchResult res in siteResults)
            {
                string url = res.Url;                 
                string pageHtml = GetPage(url);               
                QuestionInformation q = new QuestionInformation();
                q.ParseHTML(siteNum, pageHtml, message);              

                double confidence = 0;
                foreach (IRule rule in rules)
                {
                    if (rule.RuleName == "PositiveWordsFromAuthor_Rule" || rule.RuleName == "PositiveWordsFromOthers_Rule" || rule.RuleName == "KeyWords_Rule") continue;
                    var ruleValue = rule.apply(siteNum, q);
                    var weight = 0.0;
                    switch (rule.RuleName) 
                    {
                        case "Solved_Rule": weight = 0.7106; break;
                        case "NoReplies_Rule": weight = -0.8076; break;
                        case "NReplies_Rule": weight = 0.0552; break;
                        case "CodePresentInReplies_Rule": weight = 0.011; break;
                        case "AuthorReplied_Rule": weight = -0.0649; break;
                        case "AuthorMadeLastComment_Rule": weight = 0.2007; break;
                        case "KeyWords_Rule": weight = 0; break;
                        case "PositiveWordsFromAuthor_Rule": weight = 0; break;
                        case "PositiveWordsFromOthers_Rule": weight = 0; break;
                        default: throw new Exception("Wrong rule name");

                    }                    
                    res.RuleValues.Add(rule.RuleName, ruleValue);
                    confidence += ruleValue * weight;                   
                }
                confidence += 1.9304;             
                insertUrl(rankedResults, confidence, res);
            }
        }

        private void insertUrl(List<KeyValuePair<double, SearchResult>> list, double confidence, SearchResult item)
        {  
            list.Add(new KeyValuePair<double, SearchResult>(confidence, item));            
        }

        private static string GetPage(string url)
        {
            url = CheckCodeguruUrl(url);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            HttpWebResponse response = null;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (Exception e)
            {
                return "";
            }

            StreamReader resStream = new StreamReader(response.GetResponseStream());
            return resStream.ReadToEnd();
        }

        private static string CheckCodeguruUrl(string url)
        {
            Regex r = new Regex("^http://www.codeguru.com/forum/archive/index.php/t-(^<id>\\d+)\\.html$", RegexOptions.Compiled);
            var m = r.Match(url);
            if (m.Success)
            {
                return string.Format("http://www.codeguru.com/forum/showthread.php?t={0}", m.Groups["id"]);
            }
            else return url;
        }

        public static List<KeyValuePair<double, Core.SearchResult>> MakeRequest(string exceptionClassName, string codeString, string exceptionMessage, string exceptionSource, string queryString)
        {   
            //TODO Put your host name here 
            var webRequest = (HttpWebRequest)WebRequest.Create(@"http://<host name>/requestpage.aspx");
            //var webRequest = (HttpWebRequest)WebRequest.Create(@"http://localhost:65118/requestpage.aspx");

            webRequest.ContentType = "application/x-www-form-urlencoded";
            webRequest.Method = "POST";
            byte[] bytes;
            if (string.IsNullOrEmpty(queryString))
                bytes = Encoding.UTF8.GetBytes(string.Format("language={0}&query_string={1}&error={2}&selectedLine={3}&exceptionMessage={4}&exceptionSource={5}",
                                                "C#",
                                                "",
                                                exceptionClassName,
                                                codeString,
                                                exceptionMessage,
                                                exceptionSource));
            else bytes = Encoding.UTF8.GetBytes(string.Format("language={0}&query_string={1}&error={2}&selectedLine={3}&exceptionMessage={4}&exceptionSource={5}",
                                                "C#",
                                                queryString,
                                                "",
                                                "",
                                                exceptionMessage,
                                                ""));
            Stream os = null;
            try
            { // send the Post
                webRequest.ContentLength = bytes.Length;   //Count bytes to send
                os = webRequest.GetRequestStream();
                os.Write(bytes, 0, bytes.Length);         //Send it
            }

            finally
            {
                if (os != null)
                {
                    os.Close();
                }
            }

            string str;
            { // get the response              
                webRequest.Timeout = 120 * 1000;
                WebResponse webResponse = webRequest.GetResponse();
                var stream = webResponse.GetResponseStream();
                if (stream == null) return null;
                var sr = new StreamReader(stream);
                str = sr.ReadToEnd().Trim();
                sr.Close(); sr.Dispose();

            }
            if (str.StartsWith("Exception"))
                throw new Exception(str);
            if (str.Contains("<!--")) str = str.Remove(str.IndexOf("<!--"));

            var ser = new JavaScriptSerializer();
            var deserialized = ser.Deserialize<List<KeyValuePairSpecial>>(str);

            var res = new List<KeyValuePair<double, Core.SearchResult>>();
            for (int i = 0; i < deserialized.Count; i++)
            {
                res.Add(new KeyValuePair<double, Core.SearchResult>(deserialized[i].Key, deserialized[i].Value));
            }

            return res;
        }

        private class KeyValuePairSpecial
        {
            public double Key;
            public Core.SearchResult Value;
        }
    }
}
